package com.example.demo.controller;


import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.example.demo.common.Result;

import org.apache.poi.hwpf.extractor.WordExtractor;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.List;
import java.util.UUID;


@CrossOrigin
@RestController
@RequestMapping("/files")
public class FileController {
    // 文件上传存储路径
    private static final String filePath = System.getProperty("user.dir") + "/file/";
    /**
     * 上传文件
     * @param file
     * @return
     */
    @PostMapping("/upload")
    public Result upload(MultipartFile file) {
        String flag;
        // 循环生成 UUID，直到生成一个与已存在的都不同的 UUID
        do {
            flag = UUID.randomUUID().toString();
        } while (existsFlag(flag));
        // 获取文件原始名
        String fileName = file.getOriginalFilename();
        try {
            if (!FileUtil.isDirectory(filePath)) {
                // 如果没有文件夹,在项目目录下创建一个 file 文件夹
                FileUtil.mkdir(filePath);
            }
            synchronized (this) {  // 缩小同步范围
                // 文件存储形式: UUID -文件名
                FileUtil.writeBytes(file.getBytes(), filePath + flag + "_" + fileName);
            }
            System.out.println(fileName + " -- 上传成功");
            Thread.sleep(1L);
            return Result.success(flag);
        } catch (IOException e) {  // 明确捕获 IOException
            System.err.println(fileName + " -- 文件上传失败: " + e.getMessage());
            return Result.error("文件上传失败，请稍后重试");
        } catch (Exception e) {
            // 更详细的异常处理，打印异常堆栈跟踪
            e.printStackTrace();
            System.err.println(fileName + " -- 未知错误，文件上传失败");
            return Result.error("未知错误，文件上传失败");
        }
    }
    /**
     * 浏览文件
     * @param uuid
     * @param response
     */
    @GetMapping("/preview/{uuid}")
    public void previewDocument(@PathVariable String uuid, HttpServletResponse response) {
        //检查 filePath 是否是一个目录，如果不是，则创建该目录
        if (!FileUtil.isDirectory(filePath)) {
            FileUtil.mkdir(filePath);
        }
        OutputStream os = null;
        //获取 filePath 目录下的所有文件名
        List<String> fileNames = FileUtil.listFileNames(filePath);

        //通过 UUID 过滤出匹配的文件名
        String file = fileNames.stream()
                .filter(name -> name.contains(uuid))
                .findAny()
                .orElse("");

        try {
            if (StrUtil.isNotEmpty(file)) {
                // 对文件名进行 URL 编码
                String encodedFileName = URLEncoder.encode(file, "UTF-8");

                if (file.endsWith(".docx")) {
                    try {
                        //使用 WordprocessingMLPackage 加载 .docx 文件。
                        WordprocessingMLPackage wordMLPackage = WordprocessingMLPackage.load(new File(filePath + file));
                        //获取文档内容并转换为字符串
                        String textContent = wordMLPackage.getMainDocumentPart().getContent().toString();
                        // 过滤掉可能导致问题的特殊字符或格式
                       textContent = textContent.replaceAll("\\$\\$.*?\\$\\$", "");
//                      // 设置响应的内容类型和编码。
                        response.setContentType("text/plain");
                        response.setCharacterEncoding("UTF-8");
                        response.addHeader("Content-Disposition", "inline");
                        // 添加编码后的文件名到响应头
                        response.addHeader("File-Name", encodedFileName);
                        // 添加编码后的文件名到响应头
                        response.setHeader("Access-Control-Allow-Origin", "*");
                        response.setHeader("Access-Control-Expose-Headers", "File-Name");
                        os = response.getOutputStream();
                        //获取输出流并写入内容。
                        os.write(textContent.getBytes());
                        // 刷新
                        os.flush();
                        // 成功后给前端返回200状态码
                        response.setStatus(HttpServletResponse.SC_OK);
                        // 返回文件名
                    } catch (Exception e) {
                        System.err.println("加载 docx 文件时出现异常，文件路径: " + filePath + file + "，异常信息: " + e.getMessage());
                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "加载 docx 文件时出现异常");
                    }
                } else if (file.endsWith(".doc")) {
                    try {
                        //使用 WordExtractor 加载 .doc 文件。
                        WordExtractor extractor = new WordExtractor(new FileInputStream(filePath + file));
                        //获取文档内容并转换为字符串
                        String textContent = extractor.getText();
                        // 过滤掉可能导致问题的特殊字符或格式
                       textContent = textContent.replaceAll("\\$\\$.*?\\$\\$", "");
                        // 确保后端正确设置响应的内容类型和编码
                        response.setContentType("text/plain");
                        response.setCharacterEncoding("UTF-8");
                        response.addHeader("Content-Disposition", "inline");
                        // 添加编码后的文件名到响应头
                        response.addHeader("File-Name", encodedFileName);
                        response.setHeader("Access-Control-Allow-Origin", "*");
                        response.setHeader("Access-Control-Expose-Headers", "File-Name");
                        // 写出字节流
                        os = response.getOutputStream();
                        //获取输出流并写入内容。
                        os.write(textContent.getBytes());
                        //刷新
                        os.flush();
                        // 给前端返回200状态码
                        response.setStatus(HttpServletResponse.SC_OK);
                    } catch (Exception e) {
                        System.err.println("加载 doc 文件时出现异常，文件路径: " + filePath + file + "，异常信息: " + e.getMessage());
                        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "加载 doc 文件时出现异常");
                    }
                } else {
                    System.out.println("不支持的文件类型，仅支持 docx 和 doc 格式");
                    response.sendError(HttpServletResponse.SC_BAD_REQUEST, "不支持的文件类型，仅支持 docx 和 doc 格式");
                }
            }
        } catch (Exception e) {
            System.out.println("文档预览失败: " + e.getMessage());
        } finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 下载文件
     * @param uuid
     * @param response
     */
    @GetMapping("/{uuid}")
    public void avatarPath(@PathVariable String uuid, HttpServletResponse response) {
        if (!FileUtil.isDirectory(filePath)) {
            FileUtil.mkdir(filePath);
        }
        OutputStream os = null;
        List<String> fileNames = FileUtil.listFileNames(filePath);
        String avatar = fileNames.stream()
                .filter(name -> name.contains(uuid))
                .findAny()
                .orElse("");
        try {
            if (StrUtil.isNotEmpty(avatar)) {
                response.addHeader("content-Disposition", "attachment;filename=" + URLEncoder.encode(avatar, "UTF-8"));
                response.setContentType("application/octet-stream");
                byte[] bytes = FileUtil.readBytes(filePath + avatar);
                os = response.getOutputStream();
                os.write(bytes);
                os.flush();
                //成功返回200
               response.setStatus(HttpServletResponse.SC_OK);
            }
        } catch (Exception e) {
            System.out.println("文件下载失败: " + e.getMessage());
        } finally {
            if (os!= null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private boolean existsFlag(String flag) {
        // 这里模拟检查是否存在相同的 UUID，实际应用中需要根据您的存储和查询方式来实现
        // 例如，从数据库或文件系统中查找是否已存在相同的 UUID
        return false;
    }


}
